package rwt.controlpanel;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.Arrays;

import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.EmptyBorder;
import javax.swing.event.*;

import rwt.controlpanel.ServerStateChangedEvent.ServerState;

import com.jgoodies.forms.factories.FormFactory;
import com.jgoodies.forms.layout.*;

/**
 * The GUI of the RWT control panel. Swing-based.
 */
public class MainFrame extends JFrame implements ServerStateChangeListener, DocumentListener
{
	private JPanel contentPane;
	private WebServerMonitor monitor = new WebServerMonitor();
	private WebServerController serverController = new WebServerController(monitor);
	
	private JLabel lblServerStatus;
	private JButton btnStart;
	private JButton btnStop;
	private JComboBox<String> cboRunConfig;
	private JLabel lblStopServerNotice;
	
	private RwtSystemTray systemTray;
	private boolean isConfigDirty = false;
	
	/**
	 * Launch the application.
	 */
	public static void main(String[] args)
	{
		EventQueue.invokeLater(new Runnable()
		{
			public void run()
			{
				try
				{
					try
					{
						for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
						{
							if ("Nimbus".equals(info.getName()))
							{
								UIManager.setLookAndFeel(info.getClassName());
								break;
							}
						}
					}
					catch (Exception e)
					{
						// If Nimbus is not available, you can set the GUI to another look and feel.
					}
					
					MainFrame frame = new MainFrame();
					frame.setVisible(true);
				}
				catch (Exception e)
				{
					e.printStackTrace();
				}
			}
		});
	}
	
	/**
	 * Create the frame.
	 */
	public MainFrame()
	{
		setIconImage(Toolkit.getDefaultToolkit().getImage(MainFrame.class.getResource("/images/train.png")));
		setTitle("Railroad Web Throttle Server - Control Panel");
		setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
		setBounds(0, 0, 404, 336);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		this.setLocationRelativeTo(null);
		setContentPane(contentPane);
		contentPane.setLayout(new BorderLayout(0, 0));
		
		addWindowListener(new WindowAdapter()
		{
			@Override
			public void windowClosing(WindowEvent e)
			{
				System.out.println("Window closing!");
				savePrefs();
				
				if (monitor.isRunning())
				{
					System.out.println("Server is running. Moving to system tray.");
					systemTray.show();
					setVisible(false);
					systemTray.displayMessage("RWT web server is still running.", TrayIcon.MessageType.INFO);
				}
				else
				{
					System.out.println("Shutting down!");
					System.exit(0);
				}
			}
		});
		
		final JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
		tabbedPane.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				if (tabbedPane.getSelectedIndex() == 0)
				{
					if (isConfigDirty)
					{
						savePrefs();
					}
				}
			}
		});
		contentPane.add(tabbedPane, BorderLayout.CENTER);
		
		JPanel runPanel = new JPanel();
		tabbedPane.addTab("Run", null, runPanel, null);
		runPanel.setLayout(new FormLayout(new ColumnSpec[] {
				FormFactory.RELATED_GAP_COLSPEC,
				FormFactory.DEFAULT_COLSPEC,
				FormFactory.RELATED_GAP_COLSPEC,
				FormFactory.DEFAULT_COLSPEC,
				FormFactory.RELATED_GAP_COLSPEC,
				FormFactory.DEFAULT_COLSPEC,
				FormFactory.RELATED_GAP_COLSPEC,
				FormFactory.DEFAULT_COLSPEC,
				FormFactory.RELATED_GAP_COLSPEC,
				FormFactory.DEFAULT_COLSPEC,
				FormFactory.RELATED_GAP_COLSPEC,
				ColumnSpec.decode("default:grow"),},
			new RowSpec[] {
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				RowSpec.decode("max(12dlu;default)"),
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,}));
		
		JLabel lblRunConfiguration = new JLabel("Run configuration:");
		runPanel.add(lblRunConfiguration, "2, 4, right, default");
		
		String[] configNames = findConfigNames();
		String currentConfig = configNames[0];
		String configPath = currentConfig + Constants.CONFIG_SUFFIX;
		serverController.setConfigPath(configPath);
		
		ComboBoxModel<String> runConfigModel = new DefaultComboBoxModel<>(configNames);
		cboRunConfig = new JComboBox<String>(runConfigModel);
		String lastConfig = Preferences.getLastConfig();
		if (lastConfig != null && Arrays.asList(configNames).contains(lastConfig))
			cboRunConfig.setSelectedItem(lastConfig);
		
		cboRunConfig.addItemListener(new ItemListener()
		{
			@Override
			public void itemStateChanged(ItemEvent e)
			{
				if (e.getStateChange() == ItemEvent.SELECTED)
				{
					String configName = e.getItem().toString();
					String configPath = configName + Constants.CONFIG_SUFFIX;
					serverController.setConfigPath(configPath);
					Preferences.setLastConfig(configName);
				}
			}
		});
		
		runPanel.add(cboRunConfig, "6, 4, 5, 1, fill, default");
		
		JLabel lblServerStatusLabel = new JLabel("Server status:");
		runPanel.add(lblServerStatusLabel, "6, 8, right, default");
		
		lblServerStatus = new JLabel("Stopped");
		lblServerStatus.setFont(new Font("SansSerif", Font.BOLD, 14));
		runPanel.add(lblServerStatus, "8, 8, center, default");
		
		btnStart = new JButton("Start");
		btnStart.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				serverController.startServer();
			}
		});
		
		btnStart.setPreferredSize(new Dimension(80, 28));
		runPanel.add(btnStart, "6, 10");
		
		btnStop = new JButton("Stop");
		btnStop.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				serverController.stopServer();
			}
		});
		btnStop.setPreferredSize(new Dimension(80, 28));
		runPanel.add(btnStop, "8, 10");
		
		chkShowCommandWindow = new JCheckBox("Show command window");
		chkShowCommandWindow.setSelected(Preferences.getShowCommandWindow());
		chkShowCommandWindow.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				boolean show = (e.getStateChange() == ItemEvent.SELECTED);
				Preferences.setShowCommandWindow(show);
			}
		});
		
		runPanel.add(chkShowCommandWindow, "6, 12, 3, 1");
		
		btnOpenLog = new JButton("Open log");
		btnOpenLog.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try
				{
					File file = new File("log/server-log.txt");
					if (file.isFile())
					{
						String cmd = "rundll32 url.dll,FileProtocolHandler " + file.getCanonicalPath();
						Runtime.getRuntime().exec(cmd);
					}
					else
					{
						Util.showMessage("No log file exists yet.");
					}
				}
				catch (IOException ex)
				{
					Util.showError("An error occurring while opening the log file:\n" + ex.getMessage());
				}
			}
		});
		
		lblStopServerNotice = new JLabel("To change settings, please stop the server.");
		lblStopServerNotice.setFont(new Font("SansSerif", Font.PLAIN, 10));
		runPanel.add(lblStopServerNotice, "6, 14, 7, 1");
		
		separator = new JSeparator();
		runPanel.add(separator, "2, 18, 11, 1");
		runPanel.add(btnOpenLog, "6, 20");
		
		configPanel = new JPanel();
		tabbedPane.addTab("Configuration", null, configPanel, null);
		configPanel.setLayout(new FormLayout(new ColumnSpec[] {
				FormFactory.RELATED_GAP_COLSPEC,
				FormFactory.DEFAULT_COLSPEC,
				FormFactory.RELATED_GAP_COLSPEC,
				ColumnSpec.decode("default:grow"),
				FormFactory.RELATED_GAP_COLSPEC,
				FormFactory.DEFAULT_COLSPEC,},
			new RowSpec[] {
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,
				FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC,}));
		
		label = new JLabel("JDK path:");
		configPanel.add(label, "2, 4, right, default");
		
		txtJdkPath = new JTextField(Preferences.getJdkPath());
		txtJdkPath.setEnabled(true);
		txtJdkPath.setColumns(10);
		txtJdkPath.getDocument().addDocumentListener(this);
		configPanel.add(txtJdkPath, "4, 4, fill, default");
		
		btnOpenJdkPath = new JButton();
		btnOpenJdkPath.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				choosePath(Preferences.KEY_JDK_PATH);
			}
		});
		btnOpenJdkPath.setIcon(new ImageIcon(MainFrame.class.getResource("/images/Open16.gif")));
		btnOpenJdkPath.setPreferredSize(new Dimension(22, 22));
		btnOpenJdkPath.setEnabled(true);
		configPanel.add(btnOpenJdkPath, "6, 4");
		
		lblpathToThe = new JLabel("<html>Path to the root of an installed Java 7 JDK.<br>\r\nMust be 32-bit if you are using the Ada MTL.<br>\r\nExample: <b>C:\\Program Files\\Java\\jdk1.7.0</b></html>");
		lblpathToThe.setFont(new Font("Tahoma", Font.PLAIN, 10));
		configPanel.add(lblpathToThe, "4, 6");
		
		lblGnatAjisPath = new JLabel("GNAT AJIS path:");
		configPanel.add(lblGnatAjisPath, "2, 10, right, default");
		
		txtGnatAjisPath = new JTextField(Preferences.getGnatAjisPath());
		txtGnatAjisPath.setEnabled(true);
		txtGnatAjisPath.setColumns(10);
		txtGnatAjisPath.getDocument().addDocumentListener(this);
		configPanel.add(txtGnatAjisPath, "4, 10, fill, default");
		
		btnOpenGnatAjisPath = new JButton();
		btnOpenGnatAjisPath.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				choosePath(Preferences.KEY_GNAT_AJIS_PATH);
			}
		});
		btnOpenGnatAjisPath.setIcon(new ImageIcon(MainFrame.class.getResource("/images/Open16.gif")));
		btnOpenGnatAjisPath.setPreferredSize(new Dimension(22, 22));
		btnOpenGnatAjisPath.setEnabled(true);
		configPanel.add(btnOpenGnatAjisPath, "6, 10");
		
		lblpathToThe_1 = new JLabel("<html>Path to the root of your AJIS 2011 install directory.<br>\r\nExample: <b>C:\\GNAT\\2011</b></html>");
		lblpathToThe_1.setFont(new Font("Tahoma", Font.PLAIN, 10));
		configPanel.add(lblpathToThe_1, "4, 12");
		
		updateServerState(false);
		monitor.addListener(this);
		monitor.execute();
		
		systemTray = new RwtSystemTray(this);
	}
	
	private void choosePath(String key)
	{
		File file = new File("C:/Program Files/");
		if (!file.isDirectory())
			file = null;
		
		JFileChooser chooser = new JFileChooser(file);
		chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		
		if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION)
		{
			File chosenFile = chooser.getSelectedFile();
			String path = chosenFile.getAbsolutePath();
			if (key.equals(Preferences.KEY_JDK_PATH))
			{
				Preferences.setJdkPath(path);
				txtJdkPath.setText(path);
			}
			else if (key.equals(Preferences.KEY_GNAT_AJIS_PATH))
			{
				Preferences.setGnatAjisPath(path);
				txtGnatAjisPath.setText(path);
			}
		}
	}
	
	private String[] findConfigNames()
	{
		File currDir = new File(Constants.CONFIG_DIR_PATH);
		File[] propFiles = currDir.listFiles(new FilenameFilter()
		{
			@Override
			public boolean accept(File dir, String name)
			{
				return (name.toLowerCase().endsWith(Constants.CONFIG_SUFFIX) && 
						!name.equalsIgnoreCase(Constants.DEFAULT_CONFIG_FILE));
			}
		});
		
		String[] configNames = new String[propFiles.length];
		for (int i = 0; i < propFiles.length; i++)
		{
			configNames[i] = propFiles[i].getName().substring(0,
					propFiles[i].getName().length() - Constants.CONFIG_SUFFIX.length());
		}
		
		return configNames;
	}
	
	public int toInt(JTextField textField) throws IOException
	{
		try
		{
			String text = textField.getText();
			int value = Integer.parseInt(text);
			return value;
		}
		catch (NumberFormatException e)
		{
			textField.requestFocusInWindow();
			textField.selectAll();
			
			throw new IOException("Value must be an integer.");
		}
	}
	
	private static final Color RUNNING_COLOR = new Color(0, 128, 0); // Dark green
	private static final Color STOPPED_COLOR = new Color(224, 0, 0); // Red
	private JSeparator separator;
	private JCheckBox chkShowCommandWindow;
	private JPanel configPanel;
	private JLabel label;
	private JTextField txtJdkPath;
	private JButton btnOpenJdkPath;
	private JLabel lblpathToThe;
	private JLabel lblGnatAjisPath;
	private JTextField txtGnatAjisPath;
	private JButton btnOpenGnatAjisPath;
	private JLabel lblpathToThe_1;
	private JButton btnOpenLog;
	
	@Override
	public void onServerStateChanged(ServerStateChangedEvent event)
	{
		boolean isRunning = (event.getState() == ServerState.RUNNING);
		updateServerState(isRunning);
	}
	
	public void updateServerState(boolean isRunning)
	{
		lblServerStatus.setText(isRunning ? "Running" : "Stopped");
		lblServerStatus.setForeground(isRunning ? RUNNING_COLOR : STOPPED_COLOR);
		
		btnStart.setEnabled(!isRunning);
		btnStop.setEnabled(isRunning);
		
		cboRunConfig.setEnabled(!isRunning);
		lblStopServerNotice.setVisible(isRunning);
		
		chkShowCommandWindow.setEnabled(!isRunning);
	}

	@Override
	public void insertUpdate(DocumentEvent e)
	{
		isConfigDirty = true;
	}

	@Override
	public void removeUpdate(DocumentEvent e)
	{
		isConfigDirty = true;
	}

	@Override
	public void changedUpdate(DocumentEvent e)
	{
	}

	private void savePrefs()
	{
		Preferences.setJdkPath(txtJdkPath.getText());
		Preferences.setGnatAjisPath(txtGnatAjisPath.getText());
		Preferences.save();
		isConfigDirty = false;
	}

	public WebServerMonitor getServerMonitor()
	{
		return monitor;
	}

	public WebServerController getServerController()
	{
		return serverController;
	}
}
